package vaadin.lab1;

import java.io.Serializable;
import java.lang.reflect.Method;
import java.util.List;

import com.vaadin.ui.Alignment;
import com.vaadin.ui.Button;
import com.vaadin.ui.Button.ClickEvent;
import com.vaadin.ui.Component;
import com.vaadin.ui.ComponentContainer;
import com.vaadin.ui.CustomComponent;
import com.vaadin.ui.GridLayout;
import com.vaadin.ui.HorizontalLayout;
import com.vaadin.ui.VerticalLayout;

public class ApplicationChrome extends CustomComponent {

    private static final long serialVersionUID = 1L;
    private GridLayout content;
    private final List<Object> viewIds;

    private static final Method NAVIGATION_METHOD;

    static {
        try {
            NAVIGATION_METHOD = NavigationListener.class.getDeclaredMethod(
                    "navigation", new Class[] { NavigationEvent.class });
        } catch (final java.lang.NoSuchMethodException e) {
            // This should never happen
            throw new java.lang.RuntimeException(
                    "Internal error finding methods in ApplicationChrome");
        }
    }

    public ApplicationChrome(List<Object> viewIds) {
        this.viewIds = viewIds;
        content = new GridLayout(2, 2);
        content.setSizeFull();
        content.setColumnExpandRatio(1, 1.0f);
        content.setRowExpandRatio(1, 1.0f);

        setCompositionRoot(content);

        buildHeader();
        buildLeftNavigation();
    }

    private void buildHeader() {
        HorizontalLayout header = new HorizontalLayout();
        header.setSpacing(true);
        content.addComponent(header, 0, 0, 1, 0);
        content.setComponentAlignment(header, Alignment.TOP_RIGHT);
        addButtons(header);
    }

    private void buildLeftNavigation() {
        VerticalLayout layout = new VerticalLayout();
        layout.setSizeUndefined();
        layout.setSpacing(true);
        content.addComponent(layout, 0, 1);
        addButtons(layout);
    }

    public void addListener(NavigationListener listener) {
        addListener(NavigationEvent.class, listener, NAVIGATION_METHOD);
    }

    public void removeListener(NavigationListener listener) {
        removeListener(NavigationEvent.class, listener, NAVIGATION_METHOD);
    }

    @SuppressWarnings("serial")
    private void addButtons(ComponentContainer componentContainer) {
        for (final Object viewId : viewIds) {
            Button naviButton = new Button(viewId.toString());
            naviButton.addListener(new Button.ClickListener() {
                @Override
                public void buttonClick(ClickEvent event) {
                    ApplicationChrome.this.fireEvent(new NavigationEvent(
                            ApplicationChrome.this, viewId));
                }
            });
            naviButton.setWidth("150px");
            componentContainer.addComponent(naviButton);
        }
    }

    public void setCurrentView(Component newView) {
        newView.setSizeFull();
        final Component oldComponent = content.getComponent(1, 1);
        if (oldComponent == null) {
            content.addComponent(newView, 1, 1);
            content.setComponentAlignment(newView, Alignment.MIDDLE_CENTER);
        } else {
            content.replaceComponent(oldComponent, newView);
        }
    }

    public interface NavigationListener extends Serializable {
        void navigation(NavigationEvent event);
    }

    @SuppressWarnings("serial")
    public class NavigationEvent extends Component.Event {

        private final Object viewId;

        public NavigationEvent(Component source, Object viewId) {
            super(source);
            this.viewId = viewId;
        }

        public Object getViewId() {
            return viewId;
        }
    }

}
